/*
 *  Copyright 2009 Jamie Gennis <jgennis@gmail.com>.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package com.google.code.gwt.remoteaction.merge.client;

import java.util.ArrayList;
import java.util.List;

import com.google.code.gwt.remoteaction.client.AbstractActionProxy;
import com.google.code.gwt.remoteaction.client.Action;
import com.google.code.gwt.remoteaction.client.ActionExecutionService;
import com.google.code.gwt.remoteaction.client.ActionExecutionServiceAsync;
import com.google.code.gwt.remoteaction.client.ActionExecutor;
import com.google.code.gwt.remoteaction.client.ActionProxy;
import com.google.code.gwt.remoteaction.client.ActionResult;
import com.google.gwt.core.client.GWT;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * Integration tests for the GWT Remote Action Merge module.
 *
 * @author Jamie Gennis <jgennis@gmail.com>
 */
public class GwtTestRemoteActionMerge extends GWTTestCase {

  static class StallingCountingActionFilter extends AbstractActionProxy implements ActionExecutor {

    static class StalledAction {

      public Action action;

      public AsyncCallback<ActionResult> callback;

      public StalledAction(Action action, AsyncCallback<ActionResult> callback) {
        this.action = action;
        this.callback = callback;
      }
    }

    final List<StalledAction> stalledActions = new ArrayList<StalledAction>();

    int numActions = 0;

    public void execute(Action action, AsyncCallback<ActionResult> callback) {
      numActions++;
      stalledActions.add(new StalledAction(action, callback));
    }

    public void flush() {
      for (StalledAction stalledAction : stalledActions)
        proxyExecution(stalledAction.action, stalledAction.callback);
      stalledActions.clear();
    }

    public int getNumActions() {
      return numActions;
    }
  }

  int numCallsReturned;

  ActionExecutionServiceAsync actionExecutionService;

  ServiceAsync service;

  MergingActionFilter mergingActionFilter;

  StallingCountingActionFilter stallingCountingActionFilter;

  @Override
  public String getModuleName() {
    return "com.google.code.gwt.remoteaction.merge.RemoteActionMerge";
  }

  @Override
  protected void gwtSetUp() throws Exception {
    actionExecutionService =
        (ActionExecutionServiceAsync) GWT.create(ActionExecutionService.class);
    service = (ServiceAsync) GWT.create(Service.class);
    mergingActionFilter = new MergingActionFilter();
    stallingCountingActionFilter = new StallingCountingActionFilter();

    ((ActionProxy) service).setActionExecutor(mergingActionFilter);
    mergingActionFilter.setActionExecutor(stallingCountingActionFilter);
    stallingCountingActionFilter.setActionExecutor(actionExecutionService);
  }

  public void testMergingActionFilter() {
    final int NUM_CALLS_EXPECTED = 11;
    final int NUM_MERGED_CALLS_EXPECTED = 5;
    numCallsReturned = 0;

    AsyncCallback<Integer> twoCallback = new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        throw new RuntimeException(caught);
      }

      public void onSuccess(Integer result) {
        assertEquals(2, result.intValue());
        if (++numCallsReturned == NUM_CALLS_EXPECTED) {
          assertEquals(NUM_MERGED_CALLS_EXPECTED, stallingCountingActionFilter.getNumActions());
          finishTest();
        }
      }
    };
    AsyncCallback<Integer> threeCallback = new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        try {
          throw caught;
        } catch (Service.ExceptionThree ex) {
          if (++numCallsReturned == NUM_CALLS_EXPECTED) {
            assertEquals(NUM_MERGED_CALLS_EXPECTED, stallingCountingActionFilter.getNumActions());
            finishTest();
          }
        } catch (Throwable t) {
          throw new RuntimeException(t);
        }
      }

      public void onSuccess(Integer result) {
        fail("expected exception Service.ExceptionThree");
      }
    };
    AsyncCallback<Integer> fiveCallback = new AsyncCallback<Integer>() {

      public void onFailure(Throwable caught) {
        throw new RuntimeException(caught);
      }

      public void onSuccess(Integer result) {
        assertEquals(5, result.intValue());
        if (++numCallsReturned == NUM_CALLS_EXPECTED) {
          assertEquals(NUM_MERGED_CALLS_EXPECTED, stallingCountingActionFilter.getNumActions());
          finishTest();
        }
      }
    };

    service.getTwo(twoCallback);
    service.getThree(threeCallback);
    service.getTwo(twoCallback);
    service.getThree(threeCallback);
    service.getTwo(twoCallback);
    service.getTwo(twoCallback);
    service.getFive(fiveCallback);
    service.getFive(fiveCallback);
    service.getTwo(twoCallback);
    service.getTwo(twoCallback);
    service.getFive(fiveCallback);
    stallingCountingActionFilter.flush();

    delayTestFinish(10000);
  }
}
